package zydhlog

import (
	"fmt"
	"go-shop-admin/pkg/zydhfile"
	"os"
	"path"
	"runtime"
	"strconv"
	"strings"
	"time"
)

//逻辑层

/*
logWriteToScene 把日志写入到场景[通道]
param : errLevel int 错误级别
param : logFilePath string 日志存放位置
param : logMsg string 日志信息
param : arg ...any
*/
func logWriteToScene(errLevel int, logPath, logMsg string, arg ...any) {
	//开启日志
	if debugMode == 1 {
		logmsg := logContent(errLevel, runtimeCallerLayer, logMsg, arg)

		//开启后，日志写到哪的场景
		switch logToSite {
		case 1: //文件 no goroutine
			writeLogToFile(errLevel, logPath, logmsg)
		case 2: //发送到通道 ToChan
			select {
			case logChan <- newLogChan(errLevel, logPath, logmsg): //当通道没满时，走这
				fmt.Println(len(logChan))
			default: //当通道满了，走这，丢弃日志，保证不出现阻塞
			}
		default: //终端
			outputToTerminal(logmsg)
		}
	}
}

/*
logContent 组装日志的内容
param 	errLevel int 		错误级别
param 	layer int 		time.Caller()的层
param 	errInfo 	string 		错误信息
return string
*/
func logContent(errLevel, layer int, errInfo string, arg ...any) string {
	timeString := time.Now().Format(logShowTimeFormat)
	txtMap := map[int]string{
		ERR_LEVEL_NO:      "[normal]",
		ERR_LEVEL_DEBUG:   "[debug]",
		ERR_LEVEL_TRACE:   "[trace]",
		ERR_LEVEL_INFO:    "[info]",
		ERR_LEVEL_WARNING: "[warning]",
		ERR_LEVEL_ERROR:   "[error]",
		ERR_LEVEL_FATAL:   "[fatal]",
	}

	//	fmt.Sprintf("%v",error)  //把错误信息转换成string
	return timeString + "  " + getLayerCode(layer) + "\n" + txtMap[errLevel] + fmt.Sprintf("%v", errInfo) + " | " + fmt.Sprint(arg) + "\n"
}

/*
getLayerCode 获取锚点代码的方法锚点位置 包名/文件名.go/ -> 方法名()  | 行号
param 	layer int 		层，调用的层级 详细看 study/runtime.go --> runtimeCallerStudy()
return anchorPosition string  调用此方法的锚点位置

analysis:

	//pc：调用函数的一些信息	file :文件名，当前执行的文件名xxx.go/谁调用了当前函数	line : 调用的多少行	ok :如果能取到，ok == true
	//fmt.Println(pc,file,line) //8726872  D:/G-workSpace/golang/src/go-restudy/study/test.go 71
	//fmt.Println( path.Base(file),runtime.FuncForPC(pc).Name(),line  ) //test.go go-restudy/study.zydhLog 71
	//fmt.Println( pkgNameAndFuncName[0] + "/" + path.Base(file) + " -> " + pkgNameAndFuncName[1] + "()" + " | " + strconv.Itoa(line) + "行" )
	//go-restudy/study/test.go -> zydhLog() | 71行
*/
func getLayerCode(layer int) string {
	//传递参数，可以拿到当前执行程序执行隔了多少层
	pc, file, line, ok := runtime.Caller(layer)
	if !ok {
		return "use runtime.caller() failed," + "no find layer file name," + "no find layer funs name"
	}

	//获得报名和文件名
	pkgNameAndFuncName := strings.Split(runtime.FuncForPC(pc).Name(), ".")

	return pkgNameAndFuncName[0] + "/" + path.Base(file) + " -> " + pkgNameAndFuncName[1] + "()" + " | " + strconv.Itoa(line) + "行"
	//go-restudy/study/test.go -> zydhLog() | 71行
}

/*
funs  写[通道]日志到文件
param 	errLevel int 		错误级别
param 	logFilePath 	string 		日志文件存放路径
param 	errInfo 	string 		错误信息
*/
func writeLogToFile(errLevel int, logPath, logMsg string, arg ...any) {
	if errLevel >= 0 {
		// 是否切割文件 获取日志文件地址
		filePath := fileIsOpenCut(logPath)
		//打开文件 //判断文件是否存在
		f, err := zydhfile.CreatFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
		if err != nil {
			fmt.Printf("this is zydhlog/logic.go->funs writeLogToFile(); creat file false,err %v \n", err)
			return
		}
		defer f.Close()

		//直接写入字符串数据
		_, err = f.WriteString(logMsg)
		if err != nil {
			fmt.Println("this is zydhlog/logic.go->funs writeLogToFile(); log to file fail,err:", err)
		}
	}
}

// 是否开启文件切割
func fileIsOpenCut(logPath string) (filePath string) {
	//判断存放日志的目录是否存在，不存在创建
	if !zydhfile.FileOrDirPathIsExist(logFilePath) {
		if _, err := zydhfile.MkdirAll(logFilePath, 0); err != nil {
			fmt.Println(err)
			return
		}
	}
	logPath = logFilePath + logPath
	//切割文件
	switch cutFileType {
	case 0: //按照文件大小切割
		filePath = zydhfile.FileCutBySize(logPath, logFileMaxSize, cutFileByDateTimeFormat)
	case 1: //按照时间切割
		filePath = zydhfile.FileCutByTime(logPath, cutFileBySizeTimeFormat)
	default:
		filePath = logPath
	}
	return filePath
}

/*
getChanLogToFile 启动,开启goroutine处理通道数据
param : ch 只读通道  *logToChan
*/
func getChanLogToFile(ch <-chan *logToChan) {
	for {
		select {
		//当通道有值时
		case logData := <-ch:
			writeLogToFile(logData.leveL, logData.logPath, logData.logMsg)
		default:
			time.Sleep(time.Millisecond * 500) //当通道无值时，交出cpu控制权
		}
	}
}
